#pragma once
#include <iostream>
#include <unordered_map>
#include <algorithm>
#include <vector>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#include "Log.hpp"
#include "Util.hpp"

#define SEP ": "
#define WWWROOT "wwwroot"
#define HOME_PAGE "index.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"
#define PAGE_400 "404.html"
#define PAGE_404 "404.html"
#define PAGE_500 "404.html"


#define OK 200
#define BAD_REQUEST 400
#define NOT_FIND 404
#define SERVER_ERROR 500

std::string Code2Status(int code){
    switch(code){
        case 200:
            return "OK";
        case 400:
            return "BAD_REQUEST";
        case 404:
            return "NOT_FIND";
        case 500:
            return "SERVER_ERROR";
        default:
            LOG(FATAL, "Code TO Status Error!");
            return "TO_DO";
    }
}

std::string Suffix2Desc(const std::string& suffix){
    static std::unordered_map<std::string, std::string> desc = {
        {".html", "text/html"},
        {".css", "text/css"},
        {".js", "application/javascript"},
        {".jpg", "application/x-jpg"},
        {".xml", "application/xml"},
    };
    auto iter = desc.find(suffix);
    if(iter != desc.end())
        return iter->second;
    else
        return "text/html";
}

class HttpRequest
{
public:
    std::string _line;
    std::vector<std::string> _head;
    std::string _block;
    std::string _body;

    std::string _method;
    std::string _version;
    std::string _url;
    std::string _path;
    std::string _query_string;
    std::unordered_map<std::string, std::string> _head_kv;

    std::string _suffix;
    size_t _content_length;
    size_t _size;
    bool _cgi;
public:
    HttpRequest():_size(0), _content_length(0), _cgi(false){}
};

class HttpResponse
{
public:
    std::string _line;
    std::vector<std::string> _head;
    std::string _block;
    std::string _body;

    int _status_code;
    int _fd;
public:
    HttpResponse():_status_code(200), _fd(-1), _block(LINE_END){}
};

class EndPoint
{
private:
    int _sock;
    HttpRequest _request;
    HttpResponse _response;
    bool _stop = false;
private:
    bool RecvRequestLine(){
        std::string& line = _request._line;
        int s = Util::RevcLine(_sock, &line);
        if(s > 0){
            line.resize(line.size() - 1);
            LOG(INFO, "RecvLine Success : " + line);
        }
        else{
            LOG(WARNING, "RecvRequestLine Error!");
            _stop = true;
        }
        return _stop;
    }
    bool RecvRequestHead(){
        int s = 0;
        std::string str, all_head;
        while((s = Util::RevcLine(_sock, &str)) > 0){
            if(str == "\n"){
                _request._block = str;
                break;
            }
            else{
                all_head += str;
                str.resize(str.size() - 1);
                _request._head.push_back(str);
            }
        }
        if(_request._head.size() == 0 ||  _request._block.size() == 0){
            LOG(WARNING, "RecvRequestHead Error!");
            _stop = true;
        }
        else{
            LOG(INFO, "RecvRequestHead Success : \n" + all_head);
        }
        return _stop;
    }
    void PerseLine(){
        std::string rest;
        Util::CutString(_request._line, &_request._method, &rest, " ");
        Util::CutString(rest, &_request._url, &_request._version, " ");
        std::transform(_request._method.begin(), _request._method.end(), _request._method.begin(), ::toupper);
        //cout << "method: " << _request._method << endl;
    }
    void PerseHead(){
        auto& head = _request._head;
        auto it = head.begin();
        for(;it != head.end(); ++it)
        {
            std::string out1, out2;
            if(false == Util::CutString(it->c_str(), &out1, &out2, SEP)){
                LOG(WARNING, "PerseHead ERROR!");
                return;
            }
            _request._head_kv.insert({out1, out2});
        }
    }
    bool IsNeedRecvBody(){
        std::string& method = _request._method;
        if(method == "POST"){
            auto& head_kv = _request._head_kv;
            auto it = head_kv.find("Content-Length");
            if(it != head_kv.end()){
                _request._content_length = atoi(it->second.c_str());
                LOG(INFO, "Method POST, Content Length: " + it->second);
                return true;
            }
        }
        return false;
    }
    bool RecvRequesBody(){
        if(false == IsNeedRecvBody()){
            return true;
        }
        size_t content_length = _request._content_length;
        auto& body = _request._body;
        char c = '\0';
        while(content_length){
            if(recv(_sock, &c, 1, 0) > 0){
                body.push_back(c);
                --content_length;
            }
            else{
                LOG(WARNING, "RecvRequesBody ERROR!");
                return false;
            }
        }
        //cout << "body: " << body << endl;
        return true;
    }
    //处理响应正文
    int ProcessCGI(){
        LOG(INFO, "Process CGI Mthod!");
        int code = OK;
        pid_t pid;
        int out_pipe[2];
        int in_pipe[2];
        auto& path = _request._path;
        auto& method = _request._method;
        auto& query_string = _request._query_string;
        auto& content_length = _request._content_length;
        auto& body = _request._body;
        std::string method_env = "METHOD=";

        if(pipe(out_pipe) != 0){
            LOG(WARNING, "out_pipe Error!");
            code = SERVER_ERROR;
            return code;
        }
        if(pipe(in_pipe) != 0){
            LOG(WARNING, "in_pipe Error!");
            code = SERVER_ERROR;
            return code;
        }
        pid = fork();
        //child
        if(pid == 0){
            method_env += method;
            putenv((char*)method_env.c_str());
            close(out_pipe[1]);
            close(in_pipe[0]);

            if(method == "GET"){
                std::string query_string_env = "QUERY_STRING=";
                query_string_env += query_string;
                putenv((char*)query_string_env.c_str());
                LOG(INFO, "GET Method, Add QUERY_STRING Env!");
            }
            else if(method == "POST"){
                std::string content_length_env = "CONTENT_LENGTH=";
                content_length_env += std::to_string(content_length);
                //cout << "content_length: " << content_length << endl;
                putenv((char*)content_length_env.c_str());
                LOG(INFO, "POST Method, Add CONTENT_LENGTH Env!");
            }
            else{
                LOG("WARING", "Method Error!");
                code = SERVER_ERROR;
                return code;
            }
            cout << "goto execl" << endl;
            dup2(out_pipe[0], 0);
            dup2(in_pipe[1], 1);
            execl(path.c_str(), path.c_str());
            LOG(WARNING, "execl Error!");
            code = SERVER_ERROR;
            return code;
        }
        else if(pid < 0){
            LOG(WARNING, "Fork error");
            code = SERVER_ERROR;
            return code;
        }//parent
        else{
            close(out_pipe[0]);
            close(in_pipe[1]);
            if(method == "POST"){
                char* start = (char*)body.c_str();
                size_t total = 0;
                int size = 0;
                while(total < body.size() && (size = write(out_pipe[1], start + total, body.size() - total) > 0)){
                    total += size;
                }
            }
            char c = '\0';
            while(read(in_pipe[0], &c, 1) > 0){
                _response._body.push_back(c);
            }
            int status = 0;
            int ret = waitpid(pid, &status, 0);
            if(ret == pid){
                if(WIFEXITED(status)){
                    if(WEXITSTATUS(status) == 0){
                        code = OK;
                    }
                    else{
                        code = SERVER_ERROR;
                    }
                }
            }
            close(out_pipe[1]);
            close(in_pipe[0]);
        }
        return code;
    }
    // 处理响应正文
    int ProcessNoCGI(){
        int code = OK;
        auto& path = _request._path;
        int fd = open(path.c_str(), O_RDONLY);
        if(fd == -1){
            LOG(WARNING, "OPEN FILE : " + path + "Error!");
            code = NOT_FIND;
        }
        else{
            _response._fd = fd;
        }
        return code;
    }
    //处理响应头部
    void HandlerOKResponse(){
        //cout << "HandlerOKResponse !" << endl;
        std::string line = "Content-Type: ";
        line += Suffix2Desc(_request._suffix);
        line += LINE_END;
        _response._head.push_back(line);
        //cout << " _response._head.push_back(line)" << endl;
        line = "Content-Length: ";
        if(_request._cgi){
            line += std::to_string(_response._body.size());
        }
        else{
            line += std::to_string(_request._size);
        }
        line += LINE_END;
        _response._head.push_back(line);

    }
    void HandlerErrorResponse(const std::string& path){
        //cout << "HandlerErrorResponse !" << endl;
        //cout << "ERROR PATH: " << path << endl;
        _request._cgi = false;
        _response._fd = open(path.c_str(), O_RDONLY);
        if(_response._fd < 0){
            LOG(WARNING, "HandlerErrorResponse OPEN FILE ERROR!");
            return;
        }
        struct stat st;
        stat(path.c_str(), &st);
        _request._size = st.st_size;
        std::string line = "Content-Length: ";
        line += std::to_string(_request._size);
        line += LINE_END;
        _response._head.push_back(line);

        line = "Content-Type: test/html";
        line += LINE_END;
        _response._head.push_back(line);
    }
    void HandlerHttpResponse(){
        //cout << "HandlerHttpResponse !" << endl;
        auto& code = _response._status_code;
        auto& line = _response._line;
        line += HTTP_VERSION;
        line += " ";
        line += std::to_string(code);
        line += " ";
        line += Code2Status(code);
        line += LINE_END;
        //cout << "code: " << code << endl;
        std::string error_path = WWWROOT;
        error_path += '/';
        switch(code){
            case 200:
                HandlerOKResponse();
                break;
            case 400:
                error_path += PAGE_400;
                HandlerErrorResponse(error_path); 
                break;
            case 404:
                error_path += PAGE_404;
                HandlerErrorResponse(error_path);
                break;
            case 500:
                error_path += PAGE_500;
                HandlerErrorResponse(error_path);
            default:
                break;
        }
    }
public:
    EndPoint(int sock):_sock(sock){}
    bool IsStop(){
        return _stop;
    }
    void RecvRequest(){
        if((!RecvRequestLine()) && (!RecvRequestHead())){
            PerseLine();
            PerseHead();
            RecvRequesBody();
        }
    }
    void BuildResponse(){
        int& code = _response._status_code;
        auto& method = _request._method;
        auto& cgi = _request._cgi;
        auto& path = _request._path;
        struct stat st;
        size_t found = 0;
        //判断方法
        if(method == "GET"){
            //是否有带参（查询字串）
            auto it = _request._url.find("?");
            if(it != std::string::npos){
                Util::CutString(_request._url, &_request._path, &_request._query_string, "?");
                cgi = true;
            }
            else{
                _request._path = _request._url;
            }
        }
        else if(method == "POST"){
            _request._path = _request._url;
            cgi = true;
        }
        else{
            code = BAD_REQUEST;
        }
        //判断路径
        std::string tmp = path;
        path = WWWROOT;
        path += tmp;
        if(path.back() == '/'){
            path += HOME_PAGE;
        }
        //cout << "path: " << path << endl;
        if(stat(path.c_str(), &st) == 0){
            if(S_ISDIR(st.st_mode)){
                path += "/";
                path += HOME_PAGE;
                stat(path.c_str(), &st);
            }
            else{
                if((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH)){
                    cgi = true;
                }
            }
            _request._size = st.st_size;
        }
        else{
            LOG(INFO, "Http Path Not Find: " + path);
            code = NOT_FIND;
            goto END;
        }
        //处理后缀
        found = path.rfind(".");
        if(found != std::string::npos){
            _request._suffix = path.substr(found);
        }
        else{
            _request._suffix = ".html";
        }
        //处理请求
        if(cgi == true){
            code = ProcessCGI();
        }
        else{
            code = ProcessNoCGI();
        }
END:
        //建立响应报文
        HandlerHttpResponse();
    }
    void SendResponse(){
        //cout << "SendResponse!" << endl;
        //cout << "sock: " << _sock << endl;
        send(_sock, _response._line.c_str(),  _response._line.size(), 0);
        for(auto& str : _response._head){
            send(_sock, str.c_str(), str.size(), 0);
        }
        send(_sock, _response._block.c_str(), _response._block.size(), 0);
        if(_request._cgi){
            char* start = (char*)_response._body.c_str();
            int total = 0;
            int size = 0;
            while(total < _response._body.size() && (size = send(_sock, _response._body.c_str() + total, _response._body.size() - total, 0))){
                total += size;
            }
        }
        else{
            std::cout << ".............."<< _response._fd << std::endl;
            std::cout << ".............."<< _request._size << std::endl;
            sendfile(_sock, _response._fd, nullptr, _request._size);
            close(_response._fd);
        }
        LOG(INFO, "SendResponse Over!");

        ForTest();
    }
    void ForTest(){
        cout << "--------------------------------------------------------" << endl;
        cout << "_response_line: "<< _response._line;
        cout << "_response_head: ";
        for(auto& str : _response._head){
            cout << str;
        }
        cout << endl;
        cout << "--------------------------------------------------------" << endl;

    }
    ~EndPoint(){
        if(_sock < 0){
            LOG(FATAL, "EndPoint sock error!");
            exit(4);
        }
        close(_sock);
    }
};

class CallBack
{
public:
    void operator()(int sock){
#ifdef FOR_DEBUG
        // cout << "sock: " << sock << endl;
        // std::stringstream s;
        // s << "<html><body><h1>测试完成</h1></body></html>";
        // send(sock, s.str().c_str(), s.str().size(), 0);
#else
        HandlerHttpRequest(sock);
#endif
    }

    void HandlerHttpRequest(int sock){
        LOG(INFO, "Handler Begin!");
        EndPoint *ep = new EndPoint(sock);
        ep->RecvRequest();
        if(!ep->IsStop()){
            LOG(INFO, "RecvRequest OK Build And Send Response!");
            ep->BuildResponse();
            ep->SendResponse();
        }
        else{
            LOG(WARNING, "RecvRequest ERROR!");
        }
        delete ep;
        LOG(INFO, "Hander End!");
    }
};